/*
*********************************************************************************************************
*
*	模块名称 : OLED显示器驱动模块
*	文件名称 : bsp_oled.c
*	版    本 : V1.1
*	说    明 : OLED 屏(128x64)底层驱动程序，驱动芯片型号为 SSD1306.  本驱动程序适合于STM32-V5开发板.
*				OLED模块挂在FSMC总线上。
*	修改记录 :
*		版本号  日期        作者    说明
*		V1.0    2013-02-01 armfly  正式发布
*		V1.1    2015-10-14 armfly  添加24点阵，32点阵汉字和ASCII支持
*
*	Copyright (C), 2015-2020, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*/

#include "bsp.h"
#include "fonts.h"

/*
	安富莱STM32-V6开发板的OLED显示接口为FSMC总线模式。
*/

/* 下面2个宏任选 1个; 表示显示方向 */
#define DIR_NORMAL			/* 此行表示正常显示方向 */
//#define DIR_180				/* 此行表示翻转180度 */

#ifdef OLED_SPI3_EN
	/*
		SPI 模式接线定义 (只需要6根杜邦线连接)  本例子采用软件模拟SPI时序

	   【OLED模块排针】 【开发板TFT接口（STM32口线）】
	        VCC ----------- 3.3V
	        GND ----------- GND
	         CS ----------- TP_NCS   (PI10)
	        RST ----------- NRESET (也可以不连接)
	     D0/SCK ----------- TP_SCK   (PB3)
	    D1/SDIN ----------- TP_MOSI  (PB5)
	*/
	#define RCC_OLED_PORT (RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOI)		/* GPIO端口时钟 */

	#define OLED_CS_PORT	GPIOI
	#define OLED_CS_PIN		GPIO_PIN_10

	#define OLED_SCK_PORT	GPIOB
	#define OLED_SCK_PIN	GPIO_PIN_3

	#define OLED_SDIN_PORT	GPIOB
	#define OLED_SDIN_PIN	GPIO_PIN_5


	/* 定义IO = 1和 0的代码 （不用更改） */
	#define SSD_CS_1()		OLED_CS_PORT->BSRRL = OLED_CS_PIN
	#define SSD_CS_0()		OLED_CS_PORT->BSRRH = OLED_CS_PIN

	#define SSD_SCK_1()		OLED_SCK_PORT->BSRRL = OLED_SCK_PIN
	#define SSD_SCK_0()		OLED_SCK_PORT->BSRRH = OLED_SCK_PIN

	#define SSD_SDIN_1()	OLED_SDIN_PORT->BSRRL = OLED_SDIN_PIN
	#define SSD_SDIN_0()	OLED_SDIN_PORT->BSRRH = OLED_SDIN_PIN

#else

	/* 定义LCD驱动器的访问地址
		TFT接口中的RS引脚连接FSMC_A0引脚，由于是16bit模式，RS对应A1地址线，因此
		OLED_RAM的地址是+2
	*/
	#define OLED_BASE       ((uint32_t)(0x60000000))
	#define OLED_CMD		*(__IO uint16_t *)(OLED_BASE)
	#define OLED_DATA		*(__IO uint16_t *)(OLED_BASE + (1 << 2))		/* FSMC_A0接D/C, 总线配置为32bit（兼容74HC574端口） */

	/*
	1.安富莱OLED模块 80XX 模式接线定义 (需要15根杜邦线连接）
	   【OLED模块排针】 【开发板TFT接口（STM32口线）】
		    VCC ----------- 3.3V
		    GND ----------- GND
		     CS ----------- NCS
	        RST ----------- NRESET (也可以不连接)
		    D/C ----------- RS   (FSMC_A18)
		     WE ----------- NWE  (FSMC_NWE)
		     OE ----------- NOE  (FSMC_NOE)
	     D0/SCK ----------- DB0  (FSMC_D0)
		D1/SDIN ----------- DB1  (FSMC_D1)
		     D2 ----------- DB2  (FSMC_D2)
		     D3 ----------- DB3  (FSMC_D3)
		     D4 ----------- DB4  (FSMC_D4)
		     D5 ----------- DB5  (FSMC_D5)
		     D6 ----------- DB6  (FSMC_D6)
		     D7 ----------- DB7  (FSMC_D7)
	*/


#endif

/* 12864 OLED的显存镜像，占用1K字节. 共8行，每行128像素 */
static uint8_t s_ucGRAM[8][128];

/* 为了避免刷屏拉幕感太强，引入刷屏标志。
0 表示显示函数只改写缓冲区，不写屏。1 表示直接写屏（同时写缓冲区） */
static uint8_t s_ucUpdateEn = 1;

static void OLED_ConfigGPIO(void);
static void OLED_WriteCmd(uint8_t _ucCmd);
static void OLED_WriteData(uint8_t _ucData);
static void OLED_BufToPanel(void);

/*
*********************************************************************************************************
*	函 数 名: OLED_InitHard
*	功能说明: 初始化OLED屏
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_InitHard(void)
{
	OLED_ConfigGPIO();

	/* 上电延迟 */
	bsp_DelayMS(50);

	 /* 模块厂家提供初始化代码 */
	OLED_WriteCmd(0xAE);	/* 关闭OLED面板显示(休眠) */
	OLED_WriteCmd(0x00);	/* 设置列地址低4bit */
	OLED_WriteCmd(0x10);	/* 设置列地址高4bit */
	OLED_WriteCmd(0x40);	/* 设置起始行地址（低5bit 0-63）， 硬件相关*/

	OLED_WriteCmd(0x81);	/* 设置对比度命令(双字节命令），第1个字节是命令，第2个字节是对比度参数0-255 */
	OLED_WriteCmd(0xCF);	/* 设置对比度参数,缺省CF */

#ifdef DIR_NORMAL
	OLED_WriteCmd(0xA0);	/* A0 ：列地址0映射到SEG0; A1 ：列地址127映射到SEG0 */
	OLED_WriteCmd(0xC0);	/* C0 ：正常扫描,从COM0到COM63;  C8 : 反向扫描, 从 COM63至 COM0 */
#endif

#ifdef DIR_180
	OLED_WriteCmd(0xA1);	/* A0 ：列地址0映射到SEG0; A1 ：列地址127映射到SEG0 */
	OLED_WriteCmd(0xC8);	/* C0 ：正常扫描,从COM0到COM63;  C8 : 反向扫描, 从 COM63至 COM0 */
#endif

	OLED_WriteCmd(0xA6);	/* A6 : 设置正常显示模式; A7 : 设置为反显模式 */

	OLED_WriteCmd(0xA8);	/* 设置COM路数 */
	OLED_WriteCmd(0x3F);	/* 1 ->（63+1）路 */

	OLED_WriteCmd(0xD3);	/* 设置显示偏移（双字节命令）*/
	OLED_WriteCmd(0x00);	/* 无偏移 */

	OLED_WriteCmd(0xD5);	/* 设置显示时钟分频系数/振荡频率 */
	OLED_WriteCmd(0x80);	/* 设置分频系数,高4bit是分频系数，低4bit是振荡频率 */

	OLED_WriteCmd(0xD9);	/* 设置预充电周期 */
	OLED_WriteCmd(0xF1);	/* [3:0],PHASE 1; [7:4],PHASE 2; */

	OLED_WriteCmd(0xDA);	/* 设置COM脚硬件接线方式 */
	OLED_WriteCmd(0x12);

	OLED_WriteCmd(0xDB);	/* 设置 vcomh 电压倍率 */
	OLED_WriteCmd(0x40);	/* [6:4] 000 = 0.65 x VCC; 0.77 x VCC (RESET); 0.83 x VCC  */

	OLED_WriteCmd(0x8D);	/* 设置充电泵（和下个命令结合使用） */
	OLED_WriteCmd(0x14);	/* 0x14 使能充电泵， 0x10 是关闭 */
	OLED_WriteCmd(0xAF);	/* 打开OLED面板 */
}

/*
*********************************************************************************************************
*	函 数 名: OLED_DispOn
*	功能说明: 打开显示
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_DispOn(void)
{
	OLED_WriteCmd(0x8D);	/* 设置充电泵（和下个命令结合使用） */
	OLED_WriteCmd(0x14);	/* 0x14 使能充电泵， 0x10 是关闭 */
	OLED_WriteCmd(0xAF);	/* 打开OLED面板 */
}

/*
*********************************************************************************************************
*	函 数 名: OLED_DispOff
*	功能说明: 关闭显示
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_DispOff(void)
{
	OLED_WriteCmd(0x8D);	/* 设置充电泵（和下个命令结合使用）*/
	OLED_WriteCmd(0x10);	/* 0x14 使能充电泵，0x10 是关闭 */
	OLED_WriteCmd(0xAE);	/* 打开OLED面板 */
}

/*
*********************************************************************************************************
*	函 数 名: OLED_SetDir
*	功能说明: 设置显示方向
*	形    参: _ucDir = 0 表示正常方向，1表示翻转180度
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_SetDir(uint8_t _ucDir)
{
	if (_ucDir == 0)
	{
       	OLED_WriteCmd(0xA0);	/* A0 ：列地址0映射到SEG0; A1 ：列地址127映射到SEG0 */
		OLED_WriteCmd(0xC0);	/* C0 ：正常扫描,从COM0到COM63;  C8 : 反向扫描, 从 COM63至 COM0 */
	}
	else
	{
		OLED_WriteCmd(0xA1);	/* A0 ：列地址0映射到SEG0; A1 ：列地址127映射到SEG0 */
		OLED_WriteCmd(0xC8);	/* C0 ：正常扫描,从COM0到COM63;  C8 : 反向扫描, 从 COM63至 COM0 */
	}
}

/*
*********************************************************************************************************
*	函 数 名: OLED_SetContrast
*	功能说明: 设置对比度
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_SetContrast(uint8_t ucValue)
{
	OLED_WriteCmd(0x81);	/* 设置对比度命令(双字节命令），第1个字节是命令，第2个字节是对比度参数0-255 */
	OLED_WriteCmd(ucValue);	/* 设置对比度参数,缺省CF */
}

/*
*********************************************************************************************************
*	函 数 名: OLED_StartDraw
*	功能说明: 开始绘图。以后绘图函数只改写缓冲区，不改写面板显存
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_StartDraw(void)
{
	s_ucUpdateEn = 0;
}

/*
*********************************************************************************************************
*	函 数 名: OLED_EndDraw
*	功能说明: 结束绘图。缓冲区的数据刷新到面板显存。 OLED_StartDraw() 和 OLED_EndDraw() 必须成对使用
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_EndDraw(void)
{
	s_ucUpdateEn = 1;
	OLED_BufToPanel();
}

/*
*********************************************************************************************************
*	函 数 名: OLED_ClrScr
*	功能说明: 清屏
*	形    参:  _ucMode : 0 表示全黑； 0xFF表示全亮
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_ClrScr(uint8_t _ucMode)
{
	uint8_t i,j;

	for (i = 0 ; i < 8; i++)
	{
		for (j = 0 ; j < 128; j++)
		{
			s_ucGRAM[i][j] = _ucMode;
		}
	}

	if (s_ucUpdateEn == 1)
	{
		OLED_BufToPanel();
	}
}

/*
*********************************************************************************************************
*	函 数 名: OLED_BufToPanel
*	功能说明: 将缓冲区中的点阵数据写入面板
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
static void OLED_BufToPanel(void)
{
	uint8_t i,j;

	for (i = 0 ; i< 8; i++)
	{
		OLED_WriteCmd (0xB0 + i);	/* 设置页地址（0~7） */
		OLED_WriteCmd (0x00);		/* 设置列地址的低地址 */
		OLED_WriteCmd (0x10);		/* 设置列地址的高地址 */

		for (j = 0 ; j < 128; j++)
		{
			OLED_WriteData(s_ucGRAM[i][j]);
		}
	}
}

/*
*********************************************************************************************************
*	函 数 名: OLED_DispStr
*	功能说明: 在屏幕指定坐标（左上角为0，0）显示一个字符串
*	形    参:
*		_usX : X坐标，对于12864屏，范围为【0 - 127】
*		_usY : Y坐标，对于12864屏，范围为【0 - 63】
*		_ptr  : 字符串指针
*		_tFont : 字体结构体，包含颜色、背景色(支持透明)、字体代码、文字间距等参数
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_DispStr(uint16_t _usX, uint16_t _usY, char *_ptr, FONT_T *_tFont)
{
	uint32_t i;
	uint8_t code1;
	uint8_t code2;
	uint32_t address = 0;
	uint8_t buf[32 * 32 / 8];	/* 最大支持32点阵汉字 */
	uint16_t m;
	uint8_t width;
	uint8_t font_width,font_height, font_bytes;
	uint16_t x, y;
	const uint8_t *pAscDot;
	uint8_t line_bytes;
	uint8_t asc_bytes;
	uint8_t hz_bytes;
	uint8_t fGT = 0;

#ifdef USE_SMALL_FONT		
	const uint8_t *pHzDot = g_Hz12;
#else	
	uint32_t AddrHZK;
#endif	

	if (_tFont->FontCode == FC_ST_12)
	{
		font_height = 12;
		font_width = 12;
		font_bytes = 24;
		hz_bytes = 2;
		asc_bytes = 1;
		fGT = 0;
		pAscDot = g_Ascii12;
	#ifdef USE_SMALL_FONT		
		pHzDot = g_Hz12;
	#else		
		AddrHZK = HZK12_ADDR;
	#endif		
	}
	else if (_tFont->FontCode == FC_ST_16)
	{
		/* 缺省是16点阵 */
		font_height = 16;
		font_width = 16;
		font_bytes = 32;
		hz_bytes = 2;
		asc_bytes = 1;
		fGT = 0;
		pAscDot = g_Ascii16;
	#ifdef USE_SMALL_FONT		
		pHzDot = g_Hz16;
	#else
		AddrHZK = HZK16_ADDR;
	#endif
	}
	else if (_tFont->FontCode == FC_ST_24)
	{
		font_height = 24;
		font_width = 24;
		font_bytes = 72;
		hz_bytes = 3;
		asc_bytes = 2;
		fGT = 1;
		pAscDot = g_Ascii24;
	#ifdef USE_SMALL_FONT		
		pHzDot = g_Hz24;
	#else
		AddrHZK = HZK24_ADDR;
	#endif
	}
	else	// if (_tFont->FontCode == FC_ST_32)
	{
		font_height = 32;
		font_width = 32;
		font_bytes = 128;
		hz_bytes = 4;
		asc_bytes = 2;
		fGT = 1;
		pAscDot = g_Ascii32;
	#ifdef USE_SMALL_FONT		
		pHzDot = g_Hz32;
	#else
		AddrHZK = HZK32_ADDR;
	#endif
	}

	/* 开始循环处理字符 */
	while (*_ptr != 0)
	{
		code1 = *_ptr;	/* 读取字符串数据， 该数据可能是ascii代码，也可能汉字代码的高字节 */
		if (code1 < 0x80)
		{
			/* 将ascii字符点阵复制到buf */
			if (fGT == 1)	/* 根据ASCII码搜索点阵 */
			{
				m = 0;
				while(1)
				{
					address = m * (asc_bytes * font_height + 1);
					m++;
					if (code1 == pAscDot[address + 0])
					{
						address += 1;
						memcpy(buf, &pAscDot[address], asc_bytes * font_height);
						break;
					}
					else if ((pAscDot[address + 0] == 0xFF) && (pAscDot[address + 1] == 0xFF))
					{
						/* 字库搜索完毕，未找到，则填充全FF */
						memset(buf, 0xFF, font_bytes);
						break;
					}
					
				}
			}
			else	/* 全ASCII码点阵，直接计算地址 */
			{
				memcpy(buf, &pAscDot[code1 * (font_bytes / 2)], (font_bytes / 2));
			}
			width = font_width / 2;
			line_bytes = asc_bytes;
		}
		else
		{
			code2 = *++_ptr;
			if (code2 == 0)
			{
				break;
			}

			/* 计算16点阵汉字点阵地址
				ADDRESS = [(code1-0xa1) * 94 + (code2-0xa1)] * 32
				;
			*/
			#ifdef USE_SMALL_FONT
				m = 0;
				while(1)
				{
					address = m * (font_bytes + 2);
					m++;
					if ((code1 == pHzDot[address + 0]) && (code2 == pHzDot[address + 1]))
					{
						address += 2;
						memcpy(buf, &pHzDot[address], font_bytes);
						break;
					}
					else if ((pHzDot[address + 0] == 0xFF) && (pHzDot[address + 1] == 0xFF))
					{
						/* 字库搜索完毕，未找到，则填充全FF */
						memset(buf, 0xFF, font_bytes);
						break;
					}
				}
			#else	/* 用全字库 */
				/* 此处需要根据字库文件存放位置进行修改 */
				if (code1 >=0xA1 && code1 <= 0xA9 && code2 >=0xA1)
				{
					address = ((code1 - 0xA1) * 94 + (code2 - 0xA1)) * font_bytes + AddrHZK;
				}
				else if (code1 >=0xB0 && code1 <= 0xF7 && code2 >=0xA1)
				{
					address = ((code1 - 0xB0) * 94 + (code2 - 0xA1) + 846) * font_bytes + AddrHZK;
				}
				memcpy(buf, (const uint8_t *)address, font_bytes);
			#endif

				width = font_width;
				line_bytes = hz_bytes;
		}

		y = _usY;
		/* 开始刷LCD */
		for (m = 0; m < font_height; m++)	/* 字符高度 */
		{
			x = _usX;
			for (i = 0; i < width; i++)	/* 字符宽度 */
			{
				if ((buf[m * line_bytes + i / 8] & (0x80 >> (i % 8 ))) != 0x00)
				{
					OLED_PutPixel(x, y, _tFont->FrontColor);	/* 设置像素颜色为文字色 */
				}
				else
				{
					OLED_PutPixel(x, y, _tFont->BackColor);	/* 设置像素颜色为文字背景色 */
				}

				x++;
			}
			y++;
		}

		if (_tFont->Space > 0)
		{
			/* 如果文字底色按_tFont->usBackColor，并且字间距大于点阵的宽度，那么需要在文字之间填充(暂时未实现) */
		}
		_usX += width + _tFont->Space;	/* 列地址递增 */
		_ptr++;			/* 指向下一个字符 */
	}
}

/*
*********************************************************************************************************
*	函 数 名: OLED_PutPixel
*	功能说明: 画1个像素
*	形    参:
*			_usX,_usY : 像素坐标
*			_ucColor  ：像素颜色
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_PutPixel(uint16_t _usX, uint16_t _usY, uint8_t _ucColor)
{
	uint8_t ucValue;
	uint8_t ucPageAddr;
	uint8_t ucColAddr;

	const uint8_t aOrTab[8]  = {0x01, 0x02, 0x04, 0x08,0x10,0x20,0x40,0x80};
	const uint8_t aAndTab[8] = {0xFE, 0xFD, 0xFB, 0xF7,0xEF,0xDF,0xBF,0x7F};

	ucPageAddr = _usY / 8;
	ucColAddr = _usX;

	ucValue = s_ucGRAM[ucPageAddr][ucColAddr];
	if (_ucColor == 0)
	{
		ucValue &= aAndTab[_usY % 8];
	}
	else
	{
		ucValue |= aOrTab[_usY % 8];
	}
	s_ucGRAM[ucPageAddr][ucColAddr] = ucValue;

	if (s_ucUpdateEn == 1)
	{
		OLED_WriteCmd (0xB0 + ucPageAddr);					/* 设置页地址（0~7） */
		OLED_WriteCmd (0x00 + (ucColAddr & 0x0F));			/* 设置列地址的低地址 */
		OLED_WriteCmd (0x10 + ((ucColAddr >> 4) & 0x0F));	/* 设置列地址的高地址 */
		OLED_WriteData(ucValue);
	}
}

/*
*********************************************************************************************************
*	函 数 名: OLED_GetPixel
*	功能说明: 读取1个像素
*	形    参:
*			_usX,_usY : 像素坐标
*	返 回 值: 颜色值 (0, 1)
*********************************************************************************************************
*/
uint8_t OLED_GetPixel(uint16_t _usX, uint16_t _usY)
{
	uint8_t ucValue;
	uint8_t ucPageAddr;
	uint8_t ucColAddr;

	ucPageAddr = _usY / 8;
	ucColAddr = _usX;

	ucValue = s_ucGRAM[ucPageAddr][ucColAddr];
	if (ucValue & (_usY % 8))
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

/*
*********************************************************************************************************
*	函 数 名: OLED_DrawLine
*	功能说明: 采用 Bresenham 算法，在2点间画一条直线。
*	形    参:
*			_usX1, _usY1 ：起始点坐标
*			_usX2, _usY2 ：终止点Y坐标
*			_ucColor     ：颜色
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_DrawLine(uint16_t _usX1 , uint16_t _usY1 , uint16_t _usX2 , uint16_t _usY2 , uint8_t _ucColor)
{
	int32_t dx , dy ;
	int32_t tx , ty ;
	int32_t inc1 , inc2 ;
	int32_t d , iTag ;
	int32_t x , y ;

	/* 采用 Bresenham 算法，在2点间画一条直线 */

	OLED_PutPixel(_usX1 , _usY1 , _ucColor);

	/* 如果两点重合，结束后面的动作。*/
	if ( _usX1 == _usX2 && _usY1 == _usY2 )
	{
		return;
	}

	iTag = 0 ;
	/* dx = abs ( _usX2 - _usX1 ); */
	if (_usX2 >= _usX1)
	{
		dx = _usX2 - _usX1;
	}
	else
	{
		dx = _usX1 - _usX2;
	}

	/* dy = abs ( _usY2 - _usY1 ); */
	if (_usY2 >= _usY1)
	{
		dy = _usY2 - _usY1;
	}
	else
	{
		dy = _usY1 - _usY2;
	}

	if ( dx < dy )   /*如果dy为计长方向，则交换纵横坐标。*/
	{
		uint16_t temp;

		iTag = 1 ;
		temp = _usX1; _usX1 = _usY1; _usY1 = temp;
		temp = _usX2; _usX2 = _usY2; _usY2 = temp;
		temp = dx; dx = dy; dy = temp;
	}
	tx = _usX2 > _usX1 ? 1 : -1 ;    /* 确定是增1还是减1 */
	ty = _usY2 > _usY1 ? 1 : -1 ;
	x = _usX1 ;
	y = _usY1 ;
	inc1 = 2 * dy ;
	inc2 = 2 * ( dy - dx );
	d = inc1 - dx ;
	while ( x != _usX2 )     /* 循环画点 */
	{
		if ( d < 0 )
		{
			d += inc1 ;
		}
		else
		{
			y += ty ;
			d += inc2 ;
		}
		if ( iTag )
		{
			OLED_PutPixel ( y , x , _ucColor) ;
		}
		else
		{
			OLED_PutPixel ( x , y , _ucColor) ;
		}
		x += tx ;
	}
}

/*
*********************************************************************************************************
*	函 数 名: OLED_DrawPoints
*	功能说明: 采用 Bresenham 算法，绘制一组点，并将这些点连接起来。可用于波形显示。
*	形    参:
*			x, y     ：坐标数组
*			_ucColor ：颜色
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_DrawPoints(uint16_t *x, uint16_t *y, uint16_t _usSize, uint8_t _ucColor)
{
	uint16_t i;

	for (i = 0 ; i < _usSize - 1; i++)
	{
		OLED_DrawLine(x[i], y[i], x[i + 1], y[i + 1], _ucColor);
	}
}

/*
*********************************************************************************************************
*	函 数 名: OLED_DrawRect
*	功能说明: 绘制矩形。
*	形    参:
*			_usX,_usY：矩形左上角的坐标
*			_usHeight ：矩形的高度
*			_usWidth  ：矩形的宽度
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_DrawRect(uint16_t _usX, uint16_t _usY, uint8_t _usHeight, uint16_t _usWidth, uint8_t _ucColor)
{
	/*
	 ---------------->---
	|(_usX，_usY)        |
	V                    V  _usHeight
	|                    |
	 ---------------->---
		  _usWidth
	*/

	OLED_DrawLine(_usX, _usY, _usX + _usWidth - 1, _usY, _ucColor);	/* 顶 */
	OLED_DrawLine(_usX, _usY + _usHeight - 1, _usX + _usWidth - 1, _usY + _usHeight - 1, _ucColor);	/* 底 */

	OLED_DrawLine(_usX, _usY, _usX, _usY + _usHeight - 1, _ucColor);	/* 左 */
	OLED_DrawLine(_usX + _usWidth - 1, _usY, _usX + _usWidth - 1, _usY + _usHeight, _ucColor);	/* 右 */
}

/*
*********************************************************************************************************
*	函 数 名: OLED_DrawCircle
*	功能说明: 绘制一个圆，笔宽为1个像素
*	形    参:
*			_usX,_usY  ：圆心的坐标
*			_usRadius  ：圆的半径
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_DrawCircle(uint16_t _usX, uint16_t _usY, uint16_t _usRadius, uint8_t _ucColor)
{
	int32_t  D;			/* Decision Variable */
	uint32_t  CurX;		/* 当前 X 值 */
	uint32_t  CurY;		/* 当前 Y 值 */

	D = 3 - (_usRadius << 1);
	CurX = 0;
	CurY = _usRadius;

	while (CurX <= CurY)
	{
		OLED_PutPixel(_usX + CurX, _usY + CurY, _ucColor);
		OLED_PutPixel(_usX + CurX, _usY - CurY, _ucColor);
		OLED_PutPixel(_usX - CurX, _usY + CurY, _ucColor);
		OLED_PutPixel(_usX - CurX, _usY - CurY, _ucColor);
		OLED_PutPixel(_usX + CurY, _usY + CurX, _ucColor);
		OLED_PutPixel(_usX + CurY, _usY - CurX, _ucColor);
		OLED_PutPixel(_usX - CurY, _usY + CurX, _ucColor);
		OLED_PutPixel(_usX - CurY, _usY - CurX, _ucColor);

		if (D < 0)
		{
			D += (CurX << 2) + 6;
		}
		else
		{
			D += ((CurX - CurY) << 2) + 10;
			CurY--;
		}
		CurX++;
	}
}

/*
*********************************************************************************************************
*	函 数 名: OLED_DrawBMP
*	功能说明: 在LCD上显示一个BMP位图，位图点阵扫描次序：从左到右，从上到下
*	形    参:
*			_usX, _usY : 图片的坐标
*			_usHeight  ：图片高度
*			_usWidth   ：图片宽度
*			_ptr       ：单色图片点阵指针，每个像素占用1个字节
*	返 回 值: 无
*********************************************************************************************************
*/
void OLED_DrawBMP(uint16_t _usX, uint16_t _usY, uint16_t _usHeight, uint16_t _usWidth, uint8_t *_ptr)
{
	uint16_t x, y;

	for (x = 0; x < _usWidth; x++)
	{
		for (y = 0; y < _usHeight; y++)
		{
			OLED_PutPixel(_usX + x, _usY + y, *_ptr);
		}
	}
}

/*
*********************************************************************************************************
*	函 数 名: OLED_ConfigGPIO
*	功能说明: 配置OLED控制口线，设置为8位80XX总线控制模式或SPI模式
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
static void OLED_ConfigGPIO(void)
{
#ifdef OLED_SPI3_EN
	/* 1.配置GPIO,模拟SPI */
	{
		GPIO_InitTypeDef GPIO_InitStructure;

		RCC_AHB1PeriphClockCmd(RCC_OLED_PORT, ENABLE);	/* 打开GPIO时钟 */

		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;		/* 设为输出口 */
		GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;		/* 设为推挽输出模式 */
		GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	/* 上下拉电阻不使能 */
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	/* IO口最大速度 */

		GPIO_InitStructure.GPIO_Pin = OLED_CS_PIN;
		GPIO_Init(OLED_CS_PORT, &GPIO_InitStructure);

		GPIO_InitStructure.GPIO_Pin = OLED_SCK_PIN;
		GPIO_Init(OLED_SCK_PORT, &GPIO_InitStructure);

		GPIO_InitStructure.GPIO_Pin = OLED_SDIN_PIN;
		GPIO_Init(OLED_SDIN_PORT, &GPIO_InitStructure);
	}
#else
	/* 1.配置GPIO, 设置为FSMC总线。 为了和32位接口兼容，本例子也配置为32位总线，实际只用了其中8位。 */
	{
		/* bsp_fm_io 已配置fmc。 此处不必重复配置 
			bsp_InitExtIO();
		*/
	}
#endif
}

/*
*********************************************************************************************************
*	函 数 名: OLED_WriteCmd
*	功能说明: 向SSD1306发送一字节命令
*	形    参:  命令字
*	返 回 值: 无
*********************************************************************************************************
*/
static void OLED_WriteCmd(uint8_t _ucCmd)
{
#ifdef OLED_SPI3_EN
	uint8_t i;

	SSD_CS_0();

	SSD_SCK_0();
	SSD_SDIN_0();	/* 0 表示后面传送的是命令 1表示后面传送的数据 */
	SSD_SCK_1();

	for (i = 0; i < 8; i++)
	{
		if (_ucCmd & 0x80)
		{
			SSD_SDIN_1();
		}
		else
		{
			SSD_SDIN_0();
		}
		SSD_SCK_0();
		_ucCmd <<= 1;
		SSD_SCK_1();
	}

	SSD_CS_1();
#else
	OLED_CMD = _ucCmd;
#endif
}

/*
*********************************************************************************************************
*	函 数 名: OLED_WriteData
*	功能说明: 向SSD1306发送一字节数据
*	形    参:  命令字
*	返 回 值: 无
*********************************************************************************************************
*/
static void OLED_WriteData(uint8_t _ucData)
{
#ifdef OLED_SPI3_EN
	uint8_t i;

	SSD_CS_0();

	SSD_SCK_0();
	SSD_SDIN_1();	/* 0 表示后面传送的是命令 1表示后面传送的数据 */
	SSD_SCK_1();

	for (i = 0; i < 8; i++)
	{
		if (_ucData & 0x80)
		{
			SSD_SDIN_1();
		}
		else
		{
			SSD_SDIN_0();
		}
		SSD_SCK_0();
		_ucData <<= 1;
		SSD_SCK_1();
	}

	SSD_CS_1();
#else
	OLED_DATA = _ucData;
#endif
}

/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
